package a02;

import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;

import util.Pair;

public class TicTacToe extends AbstractRegularGame<Pair<Byte, Byte>> implements Serializable {
    private static final long serialVersionUID = 1L;

    public TicTacToe() {
	super((byte) 3, (byte) 3);	
    }

    @Override
    public TicTacToe clone() {
	return (TicTacToe) super.clone();
    }

    @Override
    public TicTacToe doMove(Pair<Byte, Byte> m) {
	TicTacToe result = this.clone();
	result.setPlayer(this.nextPlayer());
	result.getB()[m.fst][m.snd] = this.getPlayer();
	result.setMovesDone((byte) (this.getMovesDone() + 1));
	return result;
    }

    @Override
    public int evalState(byte p) {
	if (this.wins()) {
	    return this.lastPlayer() == p ? Integer.MAX_VALUE
		    : -Integer.MAX_VALUE;
	}
	return 0;
    }

    @Override
    public List<Pair<Byte, Byte>> moves() {

	List<Pair<Byte, Byte>> result = new LinkedList<Pair<Byte, Byte>>();

	for (byte c = 0; c < this.getColumns(); c++) {
	    for (byte r = 0; r < this.getRows(); r++) {
		if (this.getB()[c][r] == getNone()) {
		    result.add(new Pair<Byte, Byte>(c, r));
		}
	    }
	}

	return result;
    }

    @Override
    public boolean noMoreMove() {
	return this.getRows() * this.getColumns() == this.getMovesDone();
    }

    @Override
    public TicTacToe setAtPosition(byte column, byte row) {
	return this.doMove(new Pair<Byte, Byte>(column, row));
    }

    @Override
    public boolean wins(byte p) {
	return this.checkRows(p) || this.checkColumns(p)
		|| this.checkDiagonal1(p) || this.checkDiagonal2(p);
    }

    private boolean checkColumns(byte p) {
	for (byte c = 0; c < this.getColumns(); c++) {
	    for (byte r = 0; r < this.getRows(); r++) {
		if (this.getB()[c][r] != p) {
		    break;
		}
		if (r == this.getRows() - 1) {
		    return true;
		}
	    }
	}
	return false;
    }

    private boolean checkDiagonal1(byte p) {
	for (byte r = 0; r < this.getRows(); r++) {
	    if (this.getB()[r][r] != p) {
		break;
	    }
	    if (r == this.getRows() - 1) {
		return true;
	    }
	}
	return false;
    }

    private boolean checkDiagonal2(byte p) {
	for (byte r = 0; r < this.getRows(); r++) {
	    if (this.getB()[r][this.getRows() - r - 1] != p) {
		break;
	    }
	    if (r == this.getRows() - 1) {
		return true;
	    }
	}
	return false;
    }

    private boolean checkRows(byte p) {
	for (byte r = 0; r < this.getRows(); r++) {

	    for (byte c = 0; c < this.getColumns(); c++) {
		if (this.getB()[c][r] != p) {
		    break;
		}

		if (c == this.getColumns() - 1) {
		    return true;
		}
	    }
	}
	return false;
    }
}
